/*
 * @Descripttion: API封装类
 * @version: V1.0.0.1
 * @Author: zhaozhenzhuo
 * @Date: 2025-01-20 13:22:54
 * @LastEditors: zhaozhenzhuo
 * @LastEditTime: 2025-02-18 14:26:10
 */
import VideoCapture from 'video-capture';
import { v4 as uuidv4 } from 'uuid';
import {
	PlayerListOptions,
	UrlListOptions,
	MultiPlayerClassOptions,
	UrlListOptionsRequireOne
} from './types';
import { DEFAULT_CONFIG, CLASS_CONFIG } from './config';

type Constructor<T = object> = new (...args: any[]) => T;

export function APIMixin<TBase extends Constructor<any>>(Base: TBase): TBase {
	return class extends Base {
		// 录制对象
		mediaRecorder: MediaRecorder | null = null;
		// 二进制录制文件
		videoData: Blob[] = [];
		// 上次心跳时视频的时间
		lastHeartbeatTime = 0;
		// 累计播放时长
		playDuration = 0;
		// 是否已经设置了心跳
		alreadySetHeartbeatTime = false;
		heartbeatTime = 0;
		heartbeatCallback: ((currentTime: number) => void) | null = null;
		heartbeatId: string | null = null;

		// 获取播放器列表
		public getPlayerList(): MultiPlayerClassOptions['playerList'] {
			return this.playerList;
		}

		// 更新播放器尺寸
		public updatePlayerSize(width: number, height: number): void {
			this.initSize(width, height);
			// 重置容器宽高
			const container = this.el.querySelector(`.${CLASS_CONFIG.MULTI_LAYER_WRAP}`);
			if (container) {
				container.style.width = `${this.containerWidth}px`;
				container.style.height = `${this.containerHeight}px`;
			}
		}

		// 修改视频流信息
		public setUrlList(urlList: UrlListOptions[]): void {
			this.isFirst = false; // 不是初次渲染
			this.urlList = JSON.parse(JSON.stringify(urlList)).slice(0, 6);
			// 筛选urlList中不存在id或url的项，如果出现多个hasVol为true的项，只保留第一个满足条件的项
			let _existHasVol = false;
			this.urlList.forEach((item: UrlListOptionsRequireOne) => {
				if (!item.url) {
					// 如果没有url，则显示“暂无画面”
					item.placeholder = DEFAULT_CONFIG.placeholder;
				}
				// 如果没有id，则生成一个id
				if (!item.id) {
					item.id = uuidv4();
				}
				if (_existHasVol && item.hasVol) {
					item.hasVol = false;
				} else if (item.hasVol) {
					_existHasVol = true;
				}
			});

			this.hasVol = this.urlList.findIndex((item: UrlListOptions) => item.hasVol) !== -1;
			this.originHasVol = this.hasVol;

			// 找到修改后的urlList与原urlList的相同id和不同id
			// 重新设置usedId
			(this.classList as MultiPlayerClassOptions['classList']).forEach((item) => {
				const _item = this.urlList.find((i: UrlListOptions) => i.id === item.usedId);
				if (!_item) {
					item.usedId = '';
				}
			});
			for (let i = 0; i < this.classList.length; i++) {
				if (!this.classList[i].usedId && i !== this.classList.length - 1) {
					// 去后面找usedId，填补在此处
					for (let j = i + 1; j < this.classList.length; j++) {
						if (this.classList[j].usedId) {
							this.classList[i].usedId = this.classList[j].usedId;
							this.classList[j].usedId = '';
							break;
						}
					}
				}
			}
			// 移除多余的播放器和dom结构
			const multiLayerWrapRef = document.querySelector(`.${CLASS_CONFIG.MULTI_LAYER_WRAP}`);
			this.playerList.forEach((item: PlayerListOptions) => {
				const _item = this.urlList.find((i: UrlListOptions) => i.id === item.id);
				if (!_item) {
					item.player?.destroy();
					// 解绑点击和双击事件，防止内存泄漏
					if (item._dbclickEvent) {
						item.root?.removeEventListener('dblclick', item._dbclickEvent);
					}
					if (item._clickEvent) {
						item.root?.removeEventListener('click', item._clickEvent);
					}
					if (multiLayerWrapRef) {
						const _dom = multiLayerWrapRef.querySelector(`#video_${item.id}`);
						if (_dom) {
							multiLayerWrapRef.removeChild(_dom);
						}
					}
				}
			});
			// 重新设置已存在的视频的类名
			(this.classList as MultiPlayerClassOptions['classList']).forEach((item) => {
				if (item.usedId) {
					const _dom = document.querySelector(`#video_${item.usedId}`);
					if (_dom) {
						(this.classList as MultiPlayerClassOptions['classList']).forEach((i) => {
							_dom.classList.remove(i.className);
						});
						_dom.classList.add(item.className);
					}
				}
			});
			// 渲染新画面
			this.urlList.forEach((item: UrlListOptions) => {
				const _index = this.playerList.findIndex((i: PlayerListOptions) => i.id === item.id);
				if (_index !== -1) {
					this.playerList.borderStyle = item.borderStyle || '';
					this.playerList.placeholder = item.placeholder || '';
					this.playerList.deviationTime = item.deviationTime || null;
					this.playerList.currentTime = item.currentTime || null;
					this.playerList.hasVol = item.hasVol || null;
					this.playerList.progressDot = item.progressDot || [];
					// 在这里根据hasVol重新设置一下已渲染播放器的音量
					if (this.hasVol) {
						// 采用某个画面有声音的逻辑
						if (item.hasVol) {
							this.playerList[_index].player.volume = DEFAULT_CONFIG.volume;
						} else {
							this.playerList[_index].player.volume = 0;
						}
					} else {
						// 采用主画面有声音的逻辑
						const _dom = document.querySelector(`#video_${item.id}`);
						if (_dom && _dom.className.includes(CLASS_CONFIG.LARGE_CLASS)) {
							// 主画面
							this.playerList[_index].player.volume = DEFAULT_CONFIG.volume;
						} else {
							// 小画面
							this.playerList[_index].player.volume = 0;
						}
					}
				} else {
					this.playerList.push(item);
				}
			});
			// 删除playerList中多余的项
			this.playerList = this.playerList.filter(
				(item: PlayerListOptions) =>
					this.urlList.findIndex((i: UrlListOptions) => i.id === item.id) !== -1
			);
			console.log('信源切换成功');
			console.log('playerList', this.playerList);
			console.log('classList', this.classList);
			console.log('urlList', this.urlList);
			this.init();
			// 初始化功能配置
			this.initFunction();
		}

		// 设置当前时间
		public setCurrentTime(id: string | null, time: number): void {
			const _playerList: PlayerListOptions[] = this.playerList;
			const _item = _playerList.find((item) => item.id === id);
			_playerList.forEach((item) => {
				if (item.isLarge && item.player) {
					if (!id || !_item) {
						// 没传id，就给主画面直接设置时间
						item.player.currentTime = time || 0;
					} else if (item.player) {
						// 传了id，先根据主画面和有声音画面的差值计算时间后再设置
						item.player.currentTime =
							(item.deviationTime || 0) - (_item.deviationTime || 0) + (time || 0);
					}
				}
			});
		}

		// 截图
		async capture(id: string, callback: (data: string | null) => void): Promise<void> {
			try {
				const _playerList: PlayerListOptions[] = this.playerList;
				const _player = _playerList.find((item) => item.id === id);
				if (!_player) {
					throw new Error('未找到视频元素');
				}
				const video = _player.root?.querySelector('video');
				if (!video) {
					throw new Error('未找到视频元素');
				}
				// 不在直播中，暂停视频
				const playerPausedStatus = video.paused;
				if (!this.isLive) {
					video.pause();
				}
				// dataURL 捕获画面的base64
				let dataURL;
				if (this.isLive) {
					// canvas截图，不跨域
					const myCanvas = document.createElement('canvas');
					myCanvas.setAttribute('visibility', 'hidden');
					myCanvas.setAttribute('width', String(video.videoWidth));
					myCanvas.setAttribute('height', String(video.videoHeight));
					const ctx = myCanvas.getContext('2d');
					if (!ctx) {
						throw new Error('无法获取canvas的上下文');
					}
					// 绘制视频到canvas上
					ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
					// 生成图片
					dataURL = myCanvas.toDataURL('image/png');
				} else {
					// 跨域
					const capter = new VideoCapture(video.src);
					const captureMsg = await capter.capture(video.currentTime === 0 ? 1 : video.currentTime);
					dataURL = captureMsg.dataURL;
				}
				if (!playerPausedStatus) {
					video.play();
				}
				if (callback) {
					callback(dataURL);
				}
			} catch {
				if (callback) {
					callback(null);
				}
			}
		}

		// 开始录制
		public startRecord(id: string): void {
			const _playerList: PlayerListOptions[] = this.playerList;
			const _player = _playerList.find((item) => item.id === id);
			if (!_player) {
				throw new Error('未找到视频元素');
			}
			// 录制功能需要先开启声音
			if (_player.player?.muted) {
				_player.player.muted = false;
			}
			if (_player.player?.volume === 0) {
				_player.player.volume = DEFAULT_CONFIG.volume;
			}

			const video: any = _player.root?.querySelector('video');
			if (!video) {
				throw new Error('未找到视频元素');
			}

			this.mediaRecorder = new MediaRecorder(video.captureStream(30), {
				mimeType: 'video/webm; codecs=h264'
			});

			if (!this.mediaRecorder) {
				throw new Error('未能创建 MediaRecorder 实例');
			}
			this.mediaRecorder.ondataavailable = (e): void => {
				this.videoData.push(e.data);
			};

			setTimeout(() => {
				video.play();
				if (!this.mediaRecorder) {
					throw new Error('MediaRecorder 未初始化');
				}
				this.mediaRecorder.start(1000);
			}, 0);
		}

		// 结束录制
		public stopRecord(callback: (data: null | Blob[]) => void): void {
			try {
				if (!this.mediaRecorder) {
					throw new Error('MediaRecorder 未初始化');
				}
				this.mediaRecorder.stop();
				if (callback) {
					callback(this.videoData);
				}
			} catch {
				if (callback) {
					callback(null);
				}
			}
		}

		// 全屏
		public fullscreen(): void {
			const element = this.el;

			if (element.requestFullscreen) {
				element.requestFullscreen().then(() => {
					this.fullscreenSuccess();
				});
			} else if (element.mozRequestFullScreen) {
				element.mozRequestFullScreen().then(() => {
					this.fullscreenSuccess();
				});
			} else if (element.webkitRequestFullScreen) {
				element.webkitRequestFullScreen().then(() => {
					this.fullscreenSuccess();
				});
			} else if (element.msRequestFullscreen) {
				element.msRequestFullscreen().then(() => {
					this.fullscreenSuccess();
				});
			}
		}

		// 全屏设置成功后回调
		private fullscreenSuccess(): void {
			const element = this.el;

			// 设置容器宽高为视口宽高
			element.querySelector(`.${CLASS_CONFIG.MULTI_LAYER_WRAP}`).style.width = `100vw`;
			element.querySelector(`.${CLASS_CONFIG.MULTI_LAYER_WRAP}`).style.height = `100vh`;
			// 设置全屏状态
			this.isFullscreen = true;
			// 计算多画面各个播放器的宽高位置
			const _obj = this.initPlayerSize();
			// 生成多画面样式
			this.initStyle(_obj);

			const handleFullscreenChange = (): void => {
				if (document.fullscreenElement === null) {
					// 还原容器宽高
					element.querySelector(
						`.${CLASS_CONFIG.MULTI_LAYER_WRAP}`
					).style.width = `${this.containerWidth}px`;
					element.querySelector(
						`.${CLASS_CONFIG.MULTI_LAYER_WRAP}`
					).style.height = `${this.containerHeight}px`;
					// 设置全屏状态
					this.isFullscreen = false;
					// 计算多画面各个播放器的宽高位置
					const _obj = this.initPlayerSize();
					// 生成多画面样式
					this.initStyle(_obj);
					// 移除事件监听器
					element.removeEventListener('fullscreenchange', handleFullscreenChange);
				}
			};
			element.addEventListener('fullscreenchange', handleFullscreenChange);
		}

		// 退出全屏
		public exitFullscreen(): void {
			const element = this.el;
			if (element.exitFullscreen) {
				element.exitFullscreen();
			} else if (element.mozCancelFullScreen) {
				element.mozCancelFullScreen();
			} else if (element.webkitCancelFullScreen) {
				element.webkitCancelFullScreen();
			} else if (element.msExitFullscreen) {
				element.msExitFullscreen();
			}
		}

		// 设置心跳
		public setHeartbeatTime(
			options: { heartbeatTime: number; heartbeatId: string },
			callback: (currentTime: number) => void
		): void {
			try {
				if (!options || !options.heartbeatTime) {
					throw new Error('传参有误');
				}
				this.alreadySetHeartbeatTime = true;
				this.heartbeatTime = options.heartbeatTime;
				this.heartbeatCallback = callback;
				// 之前已经在timeupdate事件中调用了回调，这里只需定义回调的内容即可
				const _playerList: PlayerListOptions[] = this.playerList;
				this.heartbeatId =
					options.heartbeatId || _playerList.find((item) => item.isLarge)?.id || null;
				_playerList.forEach((playerItem) => {
					playerItem._timeupdateCallback = (): void => {
						// 秒转毫秒
						const currentTime = parseInt(String(playerItem.player?.currentTime * 1000), 0);

						// 这里给一定的延迟，防止seeking事件和timeupdate事件间隔过短，导致seeking事件内逻辑没走完就触发了下面的逻辑
						setTimeout(() => {
							// 如果当前时间大于上一次记录的时间，则将两者的差值赋给playDuration
							if (currentTime > this.lastHeartbeatTime) {
								this.playDuration += currentTime - this.lastHeartbeatTime;
							}
							// 更新上次心跳时间
							this.lastHeartbeatTime = currentTime;
							// 如果播放时长大于心跳的时间间隔，则发送请求
							if (options.heartbeatTime && this.playDuration >= options.heartbeatTime) {
								// 清空本次的播放时长
								this.playDuration = 0;
								if (callback) {
									callback(currentTime);
								}
							}
						}, 10);
					};
				});
			} catch {
				console.log('设置心跳错误');
			}
		}

		// 移除播放器
		public destroyPlayer(): void {
			const _playerList: PlayerListOptions[] = this.playerList;
			_playerList.forEach((item) => {
				item.player?.destroy();
				// 解绑点击和双击事件，防止内存泄漏
				if (item._dbclickEvent) {
					item.root?.removeEventListener('dblclick', item._dbclickEvent);
				}
				if (item._clickEvent) {
					item.root?.removeEventListener('click', item._clickEvent);
				}
			});
			if (this.syncInterval) {
				// 停止同步
				clearInterval(this.syncInterval);
				this.syncInterval = null;
			}
			// 组件销毁时停止观察
			if (this.resizeObserverArr && this.resizeObserverArr.length) {
				this.resizeObserverArr.forEach((item: ResizeObserver) => {
					item.disconnect();
				});
			}
			this.el.innerHTML = '';
		}
	};
}
